package com.zx.zxapibackend.service.impl;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import cn.hutool.captcha.generator.RandomGenerator;
import cn.hutool.core.util.DesensitizedUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.zx.zxapibackend.mapper.AuthMapper;
import com.zx.zxapibackend.mapper.InterfaceInfoMapper;
import com.zx.zxapibackend.model.domain.Auth;
import com.zx.zxapibackend.model.domain.User;
import com.zx.zxapibackend.model.dto.user.UserBindPhoneRequest;
import com.zx.zxapibackend.model.dto.user.UserLoginBySmsRequest;
import com.zx.zxapibackend.model.dto.user.UserRegisterRequest;
import com.zx.zxapibackend.security.common.UserDetailsServiceImpl;
import com.zx.zxapibackend.utils.*;
import com.zx.zxapicommon.common.BaseResponse;
import com.zx.zxapicommon.constant.CookieConstant;
import com.zx.zxapicommon.model.enums.ErrorCode;
import com.zx.zxapicommon.constant.CommonConstant;
import com.zx.zxapicommon.exception.BusinessException;
import com.zx.zxapibackend.mapper.UserMapper;
import com.zx.zxapicommon.model.dto.SmsDTO;
import com.zx.zxapibackend.model.dto.user.UserQueryRequest;
import com.zx.zxapibackend.model.enums.UserRoleEnum;
import com.zx.zxapicommon.model.to.Oauth2ResTo;
import com.zx.zxapicommon.model.vo.LoginUserVO;
import com.zx.zxapibackend.model.vo.UserVO;
import com.zx.zxapibackend.service.UserService;
import com.zx.zxapicommon.utils.CookieUtils;
import com.zx.zxapicommon.utils.ResultUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;


/**
 * 用户服务实现
 *
 * @author 张旭
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Resource
    private UserMapper userMapper;

    @Resource
    private AuthMapper authMapper;

    @Resource
    private SmsUtils smsUtils;

    @Resource
    private UserDetailsServiceImpl userDetailsService;

    @Resource
    private PasswordEncoder passwordEncoder;

    @Resource
    private TokenUtils tokenUtils;

    @Autowired
    private RedisTemplate redisTemplate;

    @Resource
    private MobileUtils mobileUtils;

    @Resource
    private GenerateAuthUtils generateAuthUtils;


    @Resource
    private InterfaceInfoMapper interfaceInfoMapper;

    @Autowired
    private Oauth2LoginUtils oauth2LoginUtils;

    @Autowired
    private MobileSignature mobileSignature;

    /**
     * 盐值，混淆密码
     */
    private static final String SALT = "yupi";

    /**
     * 图片验证码
     */
    private static final String CAPTCHA_PREFIX = "api:captchaId:";


    /**
     * 用户注册
     *
     * @param userRegisterRequest
     * @param request
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public long userRegister(UserRegisterRequest userRegisterRequest, HttpServletRequest request) {

        if (userRegisterRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请求参数为空");
        }

        String userAccount = userRegisterRequest.getUserAccount();
        String userPassword = userRegisterRequest.getUserPassword();
        String checkPassword = userRegisterRequest.getCheckPassword();
        String mobile = userRegisterRequest.getMobile();
        String code = userRegisterRequest.getCode();
        String captcha = userRegisterRequest.getCaptcha();

        // 校验
        if (StringUtils.isAnyBlank(userAccount, userPassword, checkPassword, mobile, code, captcha)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数为空");
        }
        // 图形验证码是否正确
        String signature = request.getHeader("signature");
        if (null == signature) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        String picCaptcha = String.valueOf(redisTemplate.opsForValue().get(CAPTCHA_PREFIX + signature));
        if (null == picCaptcha || mobileUtils.isCaptcha(captcha) || !captcha.equals(picCaptcha)) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "图形验证码错误或已经过期，请重新刷新验证码");
        }
        if (userAccount.length() < 4) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户账号过短");
        }
        if (userPassword.length() < 8 || checkPassword.length() < 8) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户密码过短");
        }
        // 密码和校验密码相同
        if (!userPassword.equals(checkPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "两次输入的密码不一致");
        }
        //判断手机号是否合法
        if (!MobileUtils.checkChineseMobile(mobile)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "手机号非法");
        }
        //传来的验证码和redis中的验证码是否一致
        if (!smsUtils.verifyCode(mobile, code)) {
            throw new BusinessException(ErrorCode.SMS_CODE_ERROR);
        }
        //插入数据
        synchronized (userAccount.intern()) {
            QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
            userQueryWrapper.eq("userAccount", userAccount);
            long count = this.count(userQueryWrapper);
            if (count > 0) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户名重复");
            }
            //手机号不能重复
            String getMapperPhone = userMapper.selectPhone(mobile);
            if (null != getMapperPhone) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "手机号已经被注册");
            }

            //密码加密
            String encryptPassword = passwordEncoder.encode(userPassword);

            //生成初始密钥
            String appId = String.valueOf((int) ((Math.random() * 9 + 1) * Math.pow(10, 9 - 1)));
            String accessKey = generateAuthUtils.accessKey(appId);
            String secretKey = generateAuthUtils.secretKey(appId, userAccount);
            String token = generateAuthUtils.token(appId, userAccount, accessKey, secretKey);

            Auth auth = new Auth();
            auth.setUserAccount(userAccount);
            auth.setAppid(appId);
            auth.setAccessKey(accessKey);
            auth.setSecretKey(secretKey);
            auth.setToken(token);

            // 4. 插入数据
            User user = new User();
            user.setUserAccount(userAccount);
            user.setUserPassword(encryptPassword);
            user.setMobile(mobile);
            boolean saveResult = this.save(user);
            if (!saveResult) {
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, "注册失败，数据库错误");
            }
            auth.setUserid(user.getId());
            authMapper.insert(auth);
            return user.getId();
        }
    }


    @Override
    public LoginUserVO userLogin(String userAccount, String userPassword, HttpServletRequest request, HttpServletResponse response) {
        // 1. 校验
        if (StringUtils.isAnyBlank(userAccount, userPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数为空");
        }
        if (userAccount.length() < 4) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号错误");
        }
        if (userPassword.length() < 8) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码错误");
        }


        //从UserDetailService中获取数据
        UserDetails userDetails = userDetailsService.loadUserByUsername(userAccount);

        //加密
        String password = userDetails.getPassword();
        if (!passwordEncoder.matches(userPassword, password)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号密码错误");
        }
        User user = (User) userDetails;
        user.setUserPassword(null);

        return initUserLogin(user, response);
    }

    /**
     * 获取当前登录用户
     *
     * @param request
     * @return
     */
    @Override
    public User getLoginUser(HttpServletRequest request) {

        //从request中获取当前登录用户
        Principal userPrincipal = request.getUserPrincipal();
        if (userPrincipal == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
        }
        String name = userPrincipal.getName();

        //从SecurityContextHolder获取通过认证的用户
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        Object principal = authentication.getPrincipal();
        User currentUser = (User) principal;

        //判断两者用户信息是否一致
        if (currentUser == null || currentUser.getId() == null || null == name || !currentUser.getUserAccount().equals(name)) {
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
        }

        // 从数据库查询（追求性能的话可以注释，直接走缓存）
        long userId = currentUser.getId();
        currentUser = this.getById(userId);
        if (currentUser == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
        }
        return currentUser;
    }

    /**
     * 获取当前登录用户（允许未登录）
     *
     * @param request
     * @return
     */
    @Override
    public User getLoginUserPermitNull(HttpServletRequest request) {
        // 先判断是否已登录
        Object userObj = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        User currentUser = (User) userObj;
        if (currentUser == null || currentUser.getId() == null) {
            return null;
        }
        // 从数据库查询（追求性能的话可以注释，直接走缓存）
        long userId = currentUser.getId();
        return this.getById(userId);
    }

    /**
     * 是否为管理员
     *
     * @param request
     * @return
     */
    @Override
    public boolean isAdmin(HttpServletRequest request) {
        // 仅管理员可查询
        Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        User user = (User) principal;
        return user != null && UserRoleEnum.ADMIN.getValue().equals(user.getUserRole());
    }


    @Override
    public LoginUserVO getLoginUserVO(User user) {
        if (user == null) {
            return null;
        }
        LoginUserVO loginUserVO = new LoginUserVO();
        BeanUtils.copyProperties(user, loginUserVO);
        return loginUserVO;
    }

    @Override
    public UserVO getUserVO(User user) {
        if (user == null) {
            return null;
        }
        UserVO userVO = new UserVO();
        BeanUtils.copyProperties(user, userVO);
        return userVO;
    }

    @Override
    public List<UserVO> getUserVO(List<User> userList) {
        if (CollectionUtils.isEmpty(userList)) {
            return new ArrayList<>();
        }
        return userList.stream().map(this::getUserVO).collect(Collectors.toList());
    }

    @Override
    public QueryWrapper<User> getQueryWrapper(UserQueryRequest userQueryRequest) {
        if (userQueryRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请求参数为空");
        }
        Long id = userQueryRequest.getId();

        String userName = userQueryRequest.getUserName();

        String userRole = userQueryRequest.getUserRole();
        String sortField = userQueryRequest.getSortField();
        String sortOrder = userQueryRequest.getSortOrder();
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(id != null, "id", id);

        queryWrapper.eq(StringUtils.isNotBlank(userRole), "userRole", userRole);
        queryWrapper.like(StringUtils.isNotBlank(userName), "userName", userName);
        queryWrapper.orderBy(SqlUtils.validSortField(sortField), sortOrder.equals(CommonConstant.SORT_ORDER_ASC),
                sortField);
        return queryWrapper;
    }

    /**
     * 发送短信验证码
     *
     * @param phoneNum
     * @return
     */
    @Override
    public Boolean sendCode(String phoneNum) {
        if (StringUtils.isAnyBlank(phoneNum)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "手机号为空");
        }
        boolean isPhoneNum = MobileUtils.checkChineseMobile(phoneNum);
        if (!isPhoneNum) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "手机号码格式有误");
        }

        //随机生成六位验证码
        String code = String.valueOf(RandomUtil.randomInt(100000, 999999));
        SmsDTO smsDTO = new SmsDTO(phoneNum, code);

        return smsUtils.sendSms(smsDTO);

    }

    /**
     * 检查用户登录状态
     *
     * @param request
     * @param response
     * @return
     */
    @Override
    public BaseResponse checkUserLogin(HttpServletRequest request, HttpServletResponse response) {
        //先判断是否登录,这里也是结合了SpringSecurity
        Principal userPrincipal = request.getUserPrincipal();

        if (userPrincipal != null) {
            //用户已经登录，当前登录用户直接返回
            String name = userPrincipal.getName();
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            User currentUser = (User) authentication.getPrincipal();
            if (currentUser == null || currentUser.getId() == null || null == name || !currentUser.getUserAccount().equals(name)) {
                throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
            }
            LoginUserVO loginUserVo = new LoginUserVO();
            BeanUtils.copyProperties(currentUser, loginUserVo);
            return ResultUtils.success(loginUserVo);
        } else {
            //用户未登录，看下cookie中是否携带
            Cookie[] cookies = request.getCookies();
            if (null == cookies || cookies.length == 0) {
                return ResultUtils.success(null);
            }

            String authorization = null;
            String remember = null;
            //从cookie中获取token和自动登录相关信息
            for (Cookie cookie : cookies) {
                String name = cookie.getName();
                if (CookieConstant.headAuthorization.equals(name)) {
                    authorization = cookie.getValue();
                }
                if (CookieConstant.autoLoginAuthCheck.equals(name)) {
                    remember = cookie.getValue();
                }
            }
            if (null == authorization || null == remember) {
                throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
            }
            CookieUtils cookieUtils = new CookieUtils();
            String[] strings = cookieUtils.decodeAutoLoginKey(remember);
            if (strings.length != 3) {
                throw new BusinessException(ErrorCode.ILLEGAL_ERROR, "请重新登录");
            }

            //token和自动登录的cookie中都有数据
            String sId = strings[0];
            String sUserAccount = strings[1];
            JWT jwt = JWTUtil.parseToken(authorization);
            String id = (String) jwt.getPayload("id");
            String userAccount = (String) jwt.getPayload("userAccount");
            if (!sId.equals(id) || !sUserAccount.equals(userAccount)) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "请重新登录");
            }
            User byId = this.getById(id);
            byId.setUserPassword(null);
            String phone = DesensitizedUtil.mobilePhone(byId.getMobile());
            byId.setMobile(phone);
            return ResultUtils.success(initUserLogin(byId, response));
        }
    }

    /**
     * 获取图片验证码
     *
     * @param request
     * @param response
     */
    @Override
    public void getCaptcha(HttpServletRequest request, HttpServletResponse response) {
        //随机生成 4 位验证码
        RandomGenerator randomGenerator = new RandomGenerator("0123456789", 4);
        //定义图片的显示大小
        LineCaptcha lineCaptcha = null;
        lineCaptcha = CaptchaUtil.createLineCaptcha(100, 30);

        //在前端发送请求时携带captchaId,用于标识不同的用户
        String signature = request.getHeader("signature");
        if (null == signature) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        try {
            //调用父类的setGenerator方法，设置验证码类型
            lineCaptcha.setGenerator(randomGenerator);
            //输出到页面
            lineCaptcha.write(response.getOutputStream());
            //打印日志
            log.info("captchaId:{}——————生成的验证码:{}", signature, lineCaptcha.getCode());

            //设置相应格式
            response.setContentType("image/jepg");
            response.setHeader("Pragma", "No-cache");
            //关闭流
            response.getOutputStream().close();
            //将对应的验证码存入redis中，4分钟过期
            redisTemplate.opsForValue().set(CAPTCHA_PREFIX + signature, lineCaptcha.getCode(), 4, TimeUnit.MINUTES);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 分页获取用户列表
     *
     * @param userQueryRequest
     * @return
     */
    @Override
    public BaseResponse<Page<UserVO>> listUserByPage(UserQueryRequest userQueryRequest) {
        User userQuery = new User();
        BeanUtils.copyProperties(userQueryRequest, userQuery);
        long current = userQueryRequest.getCurrent();
        long size = userQueryRequest.getPageSize();
        QueryWrapper<User> queryWrapper = new QueryWrapper<>(userQuery);
        Page<User> userPage = this.page(new Page<>(current, size), queryWrapper);
        Page<UserVO> userVOPage = new PageDTO<>(userPage.getCurrent(), userPage.getSize(), userPage.getTotal());
        List<UserVO> userVOList = userPage.getRecords().stream().map(user -> {
            UserVO userVO = new UserVO();
            BeanUtils.copyProperties(user, userVO);
            String mobile = user.getMobile();
            String newPhoneNum = mobile.substring(0, 3) + "****" + mobile.substring(7);
            userVO.setUserName(user.getNickName());
            userVO.setMobile(newPhoneNum);
            return userVO;
        }).collect(Collectors.toList());
        userVOPage.setRecords(userVOList);
        return ResultUtils.success(userVOPage);
    }

    /**
     * oauth2登录
     * @param oauth2ResTo
     * @param type
     * @param httpServletResponse
     * @return
     */
    @Override
    public BaseResponse oauth2Login(Oauth2ResTo oauth2ResTo, String type, HttpServletResponse httpServletResponse) {
        String accessToken = oauth2ResTo.getAccess_token();
        if (null == accessToken){
            return ResultUtils.error(ErrorCode.PARAMS_ERROR);
        }
        //拿到用户的信息
        LoginUserVO loginUserVo =null;
        if ("gitee".equals(type)){
            HttpResponse response = HttpRequest.get("https://gitee.com/api/v5/user?access_token=" + accessToken).execute();
            loginUserVo = oauth2LoginUtils.giteeOrGithubOauth2Login(response);
        }else {
            HttpResponse userInfo = HttpRequest.get("https://api.github.com/user")
                    .header("Authorization","Bearer "+accessToken)
                    .timeout(20000)
                    //超时，毫秒
                    .execute();
            loginUserVo = oauth2LoginUtils.giteeOrGithubOauth2Login(userInfo);
        }
        return ResultUtils.success(loginUserVo);
    }

    /**
     * 短信登录
     * @param loginBySms
     * @param response
     * @return
     */
    @Override
    public BaseResponse loginBySms(UserLoginBySmsRequest loginBySms, HttpServletResponse response) {
        String mobile = loginBySms.getMobile();
        String code = loginBySms.getCode();
        if ( null == mobile || null == code){
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //验证手机号的合法性
        if(!MobileUtils.checkChineseMobile(mobile.toString())){
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "手机号非法");
        }
        //验证用户输入的手机号和验证码是否匹配
        boolean verify = smsUtils.verifyCode(mobile, code);
        if (!verify){
            throw new BusinessException(ErrorCode.SMS_CODE_ERROR);
        }
        //验证该手机号是否完成注册
        UserDetails userDetails = userDetailsService.loadUserByUsername(mobile);
        User user=  (User) userDetails;
        user.setUserPassword(null);
        LoginUserVO loginUserVo = initUserLogin(user,response);
        return ResultUtils.success(loginUserVo);
    }


    /**
     * 获取gitee上这个项目的stars
     * @return
     */
    @Override
    public BaseResponse getGithubStars() {
        String listContent = null;
        try {
            listContent= HttpUtil.get("https://gitee.com/change404/zxapi-backend/badge/star.svg?theme=dark");
        }catch (Exception e){
            throw new BusinessException(ErrorCode.OPERATION_ERROR,"获取gitee Starts 超时");
        }
        //该操作查询时间较长
        Pattern compile = Pattern.compile("<text .*?>(.*?)</text>");
        Matcher matcher = compile.matcher(listContent);

        if (matcher.find()) {
            String text = matcher.group(1).trim();
            String stars = text.split("\\s+")[0];
            return ResultUtils.success(stars);
        }
        return ResultUtils.success("99+");
    }

    /**
     * 绑定用户手机号
     * @param userBindPhoneRequest
     * @param request
     * @return
     */
    @Override
    public BaseResponse bindPhone(UserBindPhoneRequest userBindPhoneRequest, HttpServletRequest request) {
        String captcha = userBindPhoneRequest.getCaptcha();
        Long id = userBindPhoneRequest.getId();
        String userAccount = userBindPhoneRequest.getUserAccount();
        String mobile = userBindPhoneRequest.getMobile();
        String code = userBindPhoneRequest.getCode();
        String signature = request.getHeader("signature");
        if (StringUtils.isAnyBlank(userAccount, String.valueOf(id),mobile,code,captcha,signature)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        String picCaptcha = (String) redisTemplate.opsForValue().get(CAPTCHA_PREFIX + signature);

        //验证图形验证码是否正确
        if (null == picCaptcha || MobileUtils.isCaptcha(captcha) || !captcha.equals(picCaptcha)){
            throw new BusinessException(ErrorCode.OPERATION_ERROR,"图形验证码错误或已经过期，请重新刷新验证码");
        }
        //验证手机号是否正确
        if(!MobileUtils.checkChineseMobile(mobile)){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"手机号非法");
        }
        // 手机号和验证码是否匹配
        boolean verify = smsUtils.verifyCode(mobile, code);
        if (!verify){
            throw new BusinessException(ErrorCode.SMS_CODE_ERROR);
        }
        synchronized (userAccount.intern()){
            //手机号不能重复
            String getMapperPhone = userMapper.selectPhone(mobile);
            if (null != getMapperPhone){
                throw new BusinessException(ErrorCode.PARAMS_ERROR,"手机号已经被注册");
            }
            boolean update = this.update(new UpdateWrapper<User>().eq("id", id).eq("userAccount", userAccount).set("mobile", mobile));
            if (!update){
                throw new BusinessException(ErrorCode.OPERATION_ERROR);
            }
        }
        String phone = DesensitizedUtil.mobilePhone(mobile);
        //更新全局对象中的用户信息
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        User currentUser =(User) authentication.getPrincipal();
        currentUser.setMobile(phone);
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(currentUser, null, currentUser.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        return ResultUtils.success(phone);
    }

    /**
     * 修改用户密码
     * @param password
     * @param request
     * @return
     */
    @Override
    public BaseResponse updateUserPass(String password, HttpServletRequest request) {
        if (null == password){
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Cookie[] cookies = request.getCookies();
        String value = null;
        for (Cookie cookie : cookies) {
            String name = cookie.getName();
            if ("api-mobile-signature".equals(name)){
                value = cookie.getValue();
            }
        }
        // value为空会停止执行下面的语句
        assert value != null;
        //从redis中拿到加密后的信息
        String s = (String) redisTemplate.opsForValue().get(value);
        String[] strings = mobileSignature.decodeHex(s);
        String username = strings[0];
        if (null == username){
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //更新密码
        String encode = passwordEncoder.encode(password);
        boolean update = this.update(new UpdateWrapper<User>().eq("userAccount", username).or().eq("mobile",username).set("userPassword", encode));
        if (!update){
            throw new BusinessException(ErrorCode.OPERATION_ERROR);
        }
        redisTemplate.delete(value);
        return ResultUtils.success("修改成功");
    }

    /**
     * 忘记密码部分-验证手机号和验证码输入是否正确
     * @param code
     * @param request
     * @return
     */
    @Override
    public BaseResponse authPassUserCode(String code, HttpServletRequest request) {
        if (null == code){
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Cookie[] cookies = request.getCookies();
        String value = null;
        for (Cookie cookie : cookies) {
            String name = cookie.getName();
            if ("api-mobile-signature".equals(name)){
                value = cookie.getValue();
            }
        }
        // value为空会停止执行下面的语句
        assert value != null;
        //从redis中拿到加密后的手机号
        String s = (String) redisTemplate.opsForValue().get(value);
        String[] strings = mobileSignature.decodeHex(s);
        String mobile = strings[1];
        boolean verifyCode = smsUtils.verifyCode(mobile, code);
        if (!verifyCode){
            throw new BusinessException(ErrorCode.SMS_CODE_ERROR);
        }
        return ResultUtils.success("验证成功");
    }


    /**
     * 忘记密码请求第二步，发送验证码
     * @param request
     * @return
     */
    @Override
    public BaseResponse sendPassUserCode(HttpServletRequest request) {
        Cookie[] cookies = request.getCookies();
        String value = null;
        for (Cookie cookie : cookies) {
            String name = cookie.getName();
            if ("api-mobile-signature".equals(name)){
                value = cookie.getValue();
            }
        }
        if (null == value){
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //从redis中拿到加密后的手机号
        String s = (String) redisTemplate.opsForValue().get(value);
        String[] strings = mobileSignature.decodeHex(s);
        //验证签名
        String username = strings[0];
        String signature = null;
        try {
            signature = mobileSignature.makeMobileSignature(username);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        if (!signature.equals(value)){
            throw new BusinessException(ErrorCode.FORBIDDEN_ERROR,"请求数据非法");
        }
        //发送对应的验证码
        String mobile = strings[1];
        this.sendCode(mobile);

        return ResultUtils.success("发送验证码成功");
    }

    /**
     * 用户忘记密码，返回用户注册时的手机号
     * @param username
     * @return
     */
    @Override
    public BaseResponse getPassUserType(String username,HttpServletResponse response) {
        if (null == username){
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //sql优化：由原来的查询时间1.15s降低至714ms，还可以通过建立索引来进一部降低时间
        User user = this.getOne(new QueryWrapper<User>().eq("userAccount", username).or().eq("mobile",username));
        String mobile = user.getMobile();
        if (null == mobile){
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR,"未注册");
        }
        //手机号签名时长为10分钟
        long expiryTime = System.currentTimeMillis() + 1000L * (long) 600;
        String signature =null;
        try {
            // 将手机号进行加密后，存入redis
            signature = mobileSignature.makeMobileSignature(username);
            String encryptHex = mobileSignature.makeEncryptHex(username, mobile);
            redisTemplate.opsForValue().set(signature,encryptHex,600,TimeUnit.SECONDS);
        } catch (NoSuchAlgorithmException e) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR);
        }
        Cookie cookie = new Cookie("api-mobile-signature", signature);
        cookie.setMaxAge(600);
        cookie.setPath("/");//登陆页面下才可以访问
        response.addCookie(cookie);
        String newMobile = mobile.substring(0, 3) + "****" + mobile.substring(7);
        return ResultUtils.success(newMobile);
    }


    /**
     * 初始化登录态
     *
     * @param user
     * @return
     */
    private LoginUserVO initUserLogin(User user, HttpServletResponse response) {
        //设置到Security全局对象中
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);

        //创建LoginUserVO
        LoginUserVO loginUserVO = new LoginUserVO();
        BeanUtils.copyProperties(user, loginUserVO);

        //生成token并存入redis中

        String token = tokenUtils.generateToken(String.valueOf(loginUserVO.getId()), loginUserVO.getUserAccount());

        loginUserVO.setToken(token);

        //将生成的token放入cookie中，并返回给前端
        Cookie cookie = new Cookie(CookieConstant.headAuthorization, token);
        cookie.setPath("/");
        cookie.setMaxAge(CookieConstant.expireTime);
        response.addCookie(cookie);

        //生成自动登录的cookie
        CookieUtils cookieUtils = new CookieUtils();
        String autoLoginContent = cookieUtils.generateAutoLoginContent(loginUserVO.getId().toString(), loginUserVO.getUserAccount());
        Cookie cookie1 = new Cookie(CookieConstant.autoLoginAuthCheck, autoLoginContent);
        cookie1.setPath("/");
        cookie.setMaxAge(CookieConstant.expireTime);
        response.addCookie(cookie1);

        return loginUserVO;
    }

}
